//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            PendingBreakpoint.cpp : Implementation of CPendingBreakpoint
// AUTHOR:          Vladimir Gumenuk
// DATE:            Dec-2012 -- Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xort.h"
#include "Breakpoint.h"
#include "EventBase.h"
#include "Document.h"
#include "dllmain.h"
#include "Context.h"

// CPendingBreakpoint
CPendingBreakpoint::CPendingBreakpoint()
{
    m_bpState.state = PBPS_NONE;
    m_bpState.flags = PBPSF_NONE;
    m_pSymProc      = NULL;
    m_dwInstrIP     = -1;
}

CPendingBreakpoint::~CPendingBreakpoint()
{
    ATLTRACE( "CPendingBreakpoint::~CPendingBreakpoint()\n" );
}

HRESULT CPendingBreakpoint::FinalConstruct()
{
    return S_OK;
}

void CPendingBreakpoint::FinalRelease()
{
}

HRESULT CPendingBreakpoint::Initialize( IDebugBreakpointRequest2 *pBPRequest )
{
    HRESULT hr = pBPRequest->GetRequestInfo( BPREQI_ALLFIELDS, &m_BPRequest );
    if( SUCCEEDED( hr ) )
    {
        if( m_BPRequest.dwFields & BPREQI_BPLOCATION && m_BPRequest.bpLocation.bpLocationType == BPLT_CODE_FILE_LINE )
        {
            TEXT_POSITION bpPos, bpPosEnd;
            m_BPRequest.bpLocation.bpLocation.bplocCodeFileLine.pDocPos->GetRange( &bpPos, &bpPosEnd );
            m_BPRequest.bpLocation.bpLocation.bplocCodeFileLine.pDocPos->GetFileName( &m_bsDoc );

            CComQIPtr<IXProgram> spProg( _AtlModule.getProgramNoAddRef() );
            SYMBOL_LIST *pSymTab;
            spProg->GetGlobalSymbolTable( (IUnknown**)&pSymTab );

            // Validate text position and move bp to the closest valid line.
            dword dwLineOut = 0;
            SymbolRef Proc = ValidateBreakpointPosition( *pSymTab, bpPos.dwLine, &dwLineOut );
            if( Proc.isNULL() ) // requested bp position is outside any proc
                return E_FAIL;

            m_dwInstrIP = GetInstructionIPFromSourceLine( Proc, dwLineOut );
            if( m_dwInstrIP == -1 ) // WTF?
                return E_FAIL;

            m_pSymProc = &Proc; // will store bound BP after Bind

            if( dwLineOut != bpPos.dwLine ) // need to create our own object to move position to valid lines
            {
                bpPos.dwLine = dwLineOut; // correct line in request

                SAFE_RELEASE( m_BPRequest.bpLocation.bpLocation.bplocCodeFileLine.pDocPos );

                CComObject<CDocumentPosition> *p;
                if( SUCCEEDED( hr = CComObject<CDocumentPosition>::CreateInstance( &p ) ) )
                {
                    p->AddRef();
                    p->Initialize( m_bsDoc, bpPos, bpPosEnd );

                    CComQIPtr<IDebugDocumentPosition2> spPos( p );
                    m_BPRequest.bpLocation.bpLocation.bplocCodeFileLine.pDocPos = spPos.Detach();

                    p->Release();
                }
            }
        } else
            hr = E_FAIL;
    }

    return hr;
}

void CPendingBreakpoint::SendBoundEvent( IDebugBoundBreakpoint2 *pBoundBP )
{
    CComObject<CEnumDebugBoundBreakpoints>* pBoundEnum; 
    CComObject<CEnumDebugBoundBreakpoints>::CreateInstance( &pBoundEnum ); 
    pBoundEnum->AddRef(); 

    IDebugBoundBreakpoint2* rgpBoundBP[] = { pBoundBP }; 
    pBoundEnum->Init( rgpBoundBP, &(rgpBoundBP[1]), NULL, AtlFlagCopy ); 

    CBreakpointBoundEvent* pBoundEvent = new CBreakpointBoundEvent( pBoundEnum, this );
    HRESULT hr = pBoundEvent->Send(); 
    
    pBoundEnum->Release(); 
    //ATLTRACE( "CPendingBreakpoint::SendBoundEvent() -> hr: 0x%08X\n", hr );
}

//////////////////////////////////////////////////////////////////////////
// IXBreakpoint
// This function inserts BP into proc Symbol
STDMETHODIMP CPendingBreakpoint::BindBP( IDebugBoundBreakpoint2 *pBP )
{
    if( !pBP || !m_pSymProc || !m_spBoundBP || pBP != m_spBoundBP ) // yes, pBP == m_spBoundBP
        return E_FAIL;

    m_pSymProc->SetBreakpoint( m_dwInstrIP, pBP );
    return S_OK;
}

STDMETHODIMP CPendingBreakpoint::UnbindBP( IDebugBoundBreakpoint2 *pBP )
{
    if( !pBP || !m_pSymProc || !m_spBoundBP || pBP != m_spBoundBP ) // yes, pBP == m_spBoundBP
        return E_FAIL;

    m_pSymProc->RemoveBreakpoint( m_dwInstrIP );
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
// IDebugBreakpointRequest2
STDMETHODIMP CPendingBreakpoint::GetLocationType( BP_LOCATION_TYPE *pBPLocationType )
{
    if( !pBPLocationType )
        return E_INVALIDARG;
    *pBPLocationType = m_BPRequest.bpLocation.bpLocationType;
    return S_OK;
}

STDMETHODIMP CPendingBreakpoint::GetRequestInfo( BPREQI_FIELDS dwFields, BP_REQUEST_INFO *pBPRequestInfo )
{
    if( !pBPRequestInfo )
        return E_INVALIDARG;
    m_BPRequest.CopyTo( *pBPRequestInfo );
    return S_OK;
}

////////////////////////////////////////////////////////////////////////////// 
// IDebugPendingBreakpoint2 
STDMETHODIMP CPendingBreakpoint::CanBind( IEnumDebugErrorBreakpoints2** ppErrorEnum ) 
{
    ATLTRACE( "CPendingBreakpoint::CanBind()\n" );
    HRESULT hr = E_NOTIMPL; /*, hrT;  
    if( ppErrorEnum )  
    {  
        if( m_bpState.state != PBPS_DELETED )  
        {  
            BP_REQUEST_INFO bpRequestInfo; 
            m_spBPRequest->GetRequestInfo( BPREQI_ALLFIELDS, &bpRequestInfo );
            // Verify that the breakpoint is a file/line breakpoint.  
            if( ( bpRequestInfo.dwFields & BPREQI_BPLOCATION ) &&
                ( bpRequestInfo.bpLocation.bpLocationType == BPLT_CODE_FILE_LINE ) )
            {  
                hr = S_OK;  
            }  
            else  // If the breakpoint type is not a file/line breakpoint, then the result should be an error breakpoint.  
            {  
                // If the error breakpoint member variable does not have allocated memory.
                if (!m_pErrorBP)  
                {  
                    // Create, AddRef, and initialize a CErrorBreakpoint object.  
                    if (CComObject<CErrorBreakpoint>::CreateInstance(&m_pErrorBP) == S_OK)  
                    {  
                        m_pErrorBP->AddRef();  
                        m_pErrorBP->Initialize(this);  
                    }  
                }  

                // Create a new enumerator of error breakpoints.  
                CComObject<CEnumDebugErrorBreakpoints>* pErrorEnum;  
                if (CComObject<CEnumDebugErrorBreakpoints>::CreateInstance(&pErrorEnum) == S_OK)  
                {  
                    // Get the IDebugErrorBreakpoint2 information for the   
                    // CErrorBreakpoint object.  
                    CComPtr<IDebugErrorBreakpoint2> spErrorBP;  
                    hrT = m_pErrorBP->QueryInterface(&spErrorBP);  
                    assert(hrT == S_OK);  
                    if (hrT == S_OK)  
                    {  
                        // Initialize the new enumerator of error breakpoints 
                        // with the IDebugErrorBreakpoint2 information.    
                        IDebugErrorBreakpoint2* rgpErrorBP[] = { spErrorBP.p };  
                        hrT = pErrorEnum->Init(rgpErrorBP, &(rgpErrorBP[1]), NULL, AtlFlagCopy);  
                        if (hrT == S_OK)  
                        {  
                            // Verify that the passed IEnumDebugErrorBreakpoints2   
                            // interface can be successful queried by the
                            // created CEnumDebugErrorBreakpoints object.  
                            hrT = pErrorEnum->QueryInterface(ppErrorEnum);  
                            assert(hrT == S_OK);  
                        }  
                    }  

                    // Otherwise, delete the CEnumDebugErrorBreakpoints object.  
                    if (FAILED(hrT))  
                    {  
                        delete pErrorEnum;  
                    }  
                }  

                hr = S_FALSE;  
            }  
        } else {
            hr = E_BP_DELETED;  
        }  
    } else {
        hr = E_INVALIDARG;  
    }  */

    return hr;  
}

STDMETHODIMP CPendingBreakpoint::GetState( PENDING_BP_STATE_INFO* pState ) 
{
    //ATLTRACE( "CPendingBreakpoint::GetState\n" );
    HRESULT hr;
    if( pState )
    {
        pState->state = m_bpState.state;
        pState->flags = m_bpState.flags;
        hr = S_OK;
    } else {
        hr = E_INVALIDARG;
    }
    return S_OK;
}

STDMETHODIMP CPendingBreakpoint::Virtualize( BOOL fVirtualize ) 
{
    ATLTRACE( "CPendingBreakpoint::Virtualize\n" );
    HRESULT hr;  
    if( m_bpState.state != PBPS_DELETED )  
    {  
        if( fVirtualize )  
            m_bpState.flags = PBPSF_VIRTUALIZED;  
        else  
            m_bpState.flags = PBPSF_VIRTUALIZED;  
        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  
    return hr; 
}

STDMETHODIMP CPendingBreakpoint::Enable( BOOL fEnable ) 
{
    ATLTRACE( "CPendingBreakpoint::Enable\n" );
    HRESULT hr;  
    if( m_bpState.state != PBPS_DELETED )  
    {  
        m_bpState.state = fEnable ? PBPS_ENABLED : PBPS_DISABLED;

        if( m_spBoundBP ) // pass to bound bp if any
            m_spBoundBP->Enable( fEnable );

        hr = S_OK;  
    }  else  {  
        hr = E_BP_DELETED;  
    }  

    return hr;  
}

STDMETHODIMP CPendingBreakpoint::SetCondition( BP_CONDITION bpCondition ) 
{
    ATLTRACE( "CPendingBreakpoint::SetCondition\n" );
    HRESULT hr;  
    if( m_bpState.state != BPS_DELETED )
    {  
        m_Condition = bpCondition;
        
        if( m_spBoundBP )
            m_spBoundBP->SetCondition( bpCondition );

        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  
    return hr;  
}

STDMETHODIMP CPendingBreakpoint::SetPassCount( BP_PASSCOUNT bpPassCount ) 
{
    ATLTRACE( "CPendingBreakpoint::SetPassCount\n" );
    HRESULT hr;  

    if( m_bpState.state != BPS_DELETED )
    {  
        m_PassCount.dwPassCount = bpPassCount.dwPassCount;
        m_PassCount.stylePassCount = bpPassCount.stylePassCount;

        if( m_spBoundBP )
            m_spBoundBP->SetPassCount( bpPassCount );

        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr;  
}

STDMETHODIMP CPendingBreakpoint::EnumBoundBreakpoints( IEnumDebugBoundBreakpoints2** ppEnum )
{
    ATLTRACE( "CPendingBreakpoint::EnumBoundBreakpoints\n" );
    if( !ppEnum )
        return E_INVALIDARG;
    if( !m_spBoundBP )
        return S_FALSE;

    CComObject<CEnumDebugBoundBreakpoints>* pBoundEnum; 
    HRESULT hr = CComObject<CEnumDebugBoundBreakpoints>::CreateInstance( &pBoundEnum ); 
    if( SUCCEEDED( hr ) )
    {
        pBoundEnum->AddRef(); 

        IDebugBoundBreakpoint2* rgpBoundBP[] = { m_spBoundBP }; 
        if( SUCCEEDED( hr = pBoundEnum->Init( rgpBoundBP, &(rgpBoundBP[1]), NULL, AtlFlagCopy ) ) )
        {
            hr = pBoundEnum->QueryInterface( IID_IEnumDebugBoundBreakpoints2, (void**)ppEnum );
        }

        pBoundEnum->Release(); 
    }

    return hr;
}

STDMETHODIMP CPendingBreakpoint::EnumErrorBreakpoints( BP_ERROR_TYPE bpErrorType, IEnumDebugErrorBreakpoints2** ppEnum ) 
{
    ATLTRACE( "CPendingBreakpoint::EnumErrorBreakpoints\n" );
    return E_NOTIMPL;
}

STDMETHODIMP CPendingBreakpoint::Delete() 
{
    ATLTRACE( "CPendingBreakpoint::Delete\n" );
    HRESULT hr;  

    if( m_bpState.state != PBPS_DELETED )  
    {  
        if( m_spBoundBP )  
        {  
            m_spBoundBP->Delete();  
            m_spBoundBP.Release();  
        }  

        m_BPRequest.Clear();

        // If the pending breakpoint did not bind and has an associated error breakpoint, if any
        //m_spErrorBP.Release();  

        // Set the PENDING_BP_STATE in the PENDING_BP_STATE_INFO structure to deleted.   
        m_bpState.state = PBPS_DELETED;
        hr = S_OK;
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr; 
}

STDMETHODIMP CPendingBreakpoint::GetBreakpointRequest( IDebugBreakpointRequest2** ppBPRequest ) 
{ 
    ATLTRACE( "CPendingBreakpoint::GetBreakpointRequest()\n" );
    HRESULT hr;  
    if( ppBPRequest )
    {
        if( m_bpState.state != BPS_DELETED )
        {  
            hr = this->QueryInterface( IID_IDebugBreakpointRequest2, (void**)ppBPRequest );  
        } else {  
            hr = E_BP_DELETED;  
        }
    } else {
        hr = E_INVALIDARG;
    }

    return hr;  
} 

STDMETHODIMP CPendingBreakpoint::Bind() 
{ 
    ATLTRACE( "CPendingBreakpoint::Bind() -> internal bound BP: 0x%08X\n", m_spBoundBP );
    if( m_bpState.state == BPS_DELETED )
        return E_BP_DELETED;

    if( m_spBoundBP ) // already bound?
        return E_FAIL;

    HRESULT hr;

    // Get the position in the document 
    if( SUCCEEDED( hr = m_BPRequest.bpLocation.bpLocation.bplocCodeFileLine.pDocPos->GetRange( &m_BPPos, NULL ) ) )
    {
        ATLTRACE( "Bind BP: %S, line: %d, col: %d\n", m_bsDoc, m_BPPos.dwLine, m_BPPos.dwColumn );

        // Create a bound breakpoint 
        CComObject<CBoundBreakpoint> *pBoundBP;
        if( SUCCEEDED( hr = CComObject<CBoundBreakpoint>::CreateInstance( &pBoundBP ) ) )
        {
            pBoundBP->AddRef(); 
                        
            if( SUCCEEDED( hr = pBoundBP->Initialize( this, m_bsDoc, &m_BPPos ) ) )
            {
                m_spBoundBP = pBoundBP; // store it internally

                // save BP in program
                m_spBoundBP->Enable( TRUE ); // enable it and store in program

                SendBoundEvent( pBoundBP ); // notify SDM that BP is bounded
            }

            pBoundBP->Release(); 
        }
    }

    ATLTRACE( "CPendingBreakpoint::Bind() -> internal bound BP: 0x%08X, hr: 0x%08X\n", m_spBoundBP, hr );
    return hr; 
} 

//////////////////////////////////////////////////////////////////////////
//
CBoundBreakpoint::CBoundBreakpoint()
{
    m_bpState    = BPS_NONE;
    m_dwIP       = -1;
    m_dwHitCount = 0;
    ZeroMemory( &m_bpResolutionInfo, sizeof(m_bpResolutionInfo) );
}

CBoundBreakpoint::~CBoundBreakpoint()
{
    SAFE_RELEASE( m_bpResolutionInfo.bpResLocation.bpResLocation.bpresCode.pCodeContext );
    ATLTRACE( "CBoundBreakpoint::~CBoundBreakpoint()\n" );
}

HRESULT CBoundBreakpoint::FinalConstruct()
{
    return S_OK;
}

void CBoundBreakpoint::FinalRelease()
{

}

STDMETHODIMP CBoundBreakpoint::Initialize( IDebugPendingBreakpoint2 *pPending, LPCWSTR pszFileName, TEXT_POSITION *pPos )
{
    USES_CONVERSION;

    m_spPendingBP   = pPending;  
    m_BPPos         = *pPos;

    m_bpResolutionInfo.bpResLocation.bpType = BPT_CODE; // support only code BP
    m_bpResolutionInfo.dwFields = BPRESI_BPRESLOCATION; 

    HRESULT hr;

    CComObject<CDocument> *p;
    if( SUCCEEDED( hr = CComObject<CDocument>::CreateInstance( &p ) ) )
    {
        p->AddRef();
        p->Initialize( OLE2A( pszFileName ) );
        // this object the is only needed here to return bound BP position to the IDE and no more...
        p->SetPosition( &m_BPPos );

        CComQIPtr<IDebugCodeContext2> spCodeCtx( p ); // attach context to BP
        m_bpResolutionInfo.bpResLocation.bpResLocation.bpresCode.pCodeContext = spCodeCtx.Detach();

        p->Release();
        ATLTRACE( "CBoundBreakpoint::Initialize() -> CodeContext set\n" );
    } 

    ATLTRACE( "CBoundBreakpoint::Initialize() -> hr: 0x%08X\n", hr );
    return hr;  
}

STDMETHODIMP CBoundBreakpoint::SetIP( DWORD dwIP )
{
    m_dwIP = dwIP;
    return S_OK;
}

STDMETHODIMP CBoundBreakpoint::GetIP( DWORD *pdwIP )
{
    *pdwIP = m_dwIP;
    return S_OK;
}

////////////////////////////////////////////////////////////////////////////// 
// IDebugBoundBreakpoint2 
STDMETHODIMP CBoundBreakpoint::GetState( BP_STATE* pState )  
{
    //ATLTRACE( "CBoundBreakpoint::GetState()\n" );
    HRESULT hr;
    if( pState )
    {
        *pState = m_bpState;
        hr = S_OK;
    } else {
        hr = E_INVALIDARG;
    }
    //ATLTRACE( "CBoundBreakpoint::GetState() -> LEAVE, hr: 0x%08X\n", hr );
    return hr;
}

STDMETHODIMP CBoundBreakpoint::GetHitCount( DWORD* pdwHitCount )  
{
    //ATLTRACE( "CBoundBreakpoint::GetHitCount()\n" );
    HRESULT hr;  

    if( pdwHitCount )
    {
        // Verify that the bound breakpoint has not been deleted. If deleted, then return hr = E_BP_DELETED.  
        if( m_bpState != BPS_DELETED )
        {  
            *pdwHitCount = m_dwHitCount;
            hr = S_OK;  
        } else {  
            hr = E_BP_DELETED;  
        }
    } else {
        hr = E_INVALIDARG;
    }

    return hr;
}

STDMETHODIMP CBoundBreakpoint::Enable( BOOL fEnable )  
{
    //ATLTRACE( "CBoundBreakpoint::Enable()\n" );
    HRESULT hr;  
    IXProgram *pProg = _AtlModule.getXProgramNoAddRef();

    if( m_bpState != BPS_DELETED )  
    {  
        CComQIPtr<IXBreakpoint> spXBP( m_spPendingBP );

        if( fEnable && m_bpState != BPS_ENABLED )
        {  
            if( SUCCEEDED( hr = spXBP->BindBP( this ) ) )
            {  
                m_bpState = BPS_ENABLED;
            }  
        }  
        else if( !fEnable && m_bpState != BPS_DISABLED )  // If the breakpoint is supposed to be, but has not already been, disabled, 
        {                                                // then have the interpreter remove the breakpoint.  
            if( SUCCEEDED( hr = spXBP->UnbindBP( this ) ) )
            {  
                m_bpState = BPS_DISABLED;
            }  
        } else {  
            hr = S_FALSE;  
        }  
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr; 
}

STDMETHODIMP CBoundBreakpoint::SetHitCount( DWORD dwHitCount )  
{
    //ATLTRACE( "CBoundBreakpoint::SetHitCount()\n" );
    HRESULT hr;
    if( m_bpState != BPS_DELETED )
    {  
        m_dwHitCount = dwHitCount;
        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }
    return hr;
}

STDMETHODIMP CBoundBreakpoint::SetCondition( BP_CONDITION bpCondition )  
{
    //ATLTRACE( "CBoundBreakpoint::SetCondition()\n" );
    HRESULT hr;  

    if( m_bpState != BPS_DELETED )
    {  
        m_Condition = bpCondition;
        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr;  
}

STDMETHODIMP CBoundBreakpoint::SetPassCount( BP_PASSCOUNT bpPassCount )  
{
    //ATLTRACE( "CBoundBreakpoint::SetPassCount()\n" );
    HRESULT hr;  

    if( m_bpState != BPS_DELETED )
    {  
        m_PassCount.dwPassCount = bpPassCount.dwPassCount;
        m_PassCount.stylePassCount = bpPassCount.stylePassCount;
        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr;  
}

STDMETHODIMP CBoundBreakpoint::Delete()  
{
    //ATLTRACE( "CBoundBreakpoint::Delete()\n" );
    HRESULT hr;  

    if( m_bpState != BPS_DELETED )
    {  
        CComQIPtr<IXBreakpoint> spXBP( m_spPendingBP );
        spXBP->UnbindBP( this );

        m_spPendingBP.Release();  // break cross reference :)
        m_Condition.Clear();

        m_bpState = BPS_DELETED;  // Change the state of the breakpoint to BPS_DELETED.
        hr = S_OK;  
    } else {  
        hr = E_BP_DELETED;  
    }  

    return hr;  
} 

STDMETHODIMP CBoundBreakpoint::GetPendingBreakpoint( IDebugPendingBreakpoint2** ppPendingBreakpoint )  
{ 
    //ATLTRACE( "CBoundBreakpoint::GetPendingBreakpoint()\n" );
    HRESULT hr;  
    if( ppPendingBreakpoint )  
    {  
        if( m_bpState != BPS_DELETED )  
        {  
            hr = m_spPendingBP->QueryInterface( IID_IDebugPendingBreakpoint2, (void**)ppPendingBreakpoint );  
        } else {  
            hr = E_BP_DELETED;  
        }  
    } else {  
        hr = E_INVALIDARG;  
    }
    //ATLTRACE( "CBoundBreakpoint::GetPendingBreakpoint() -> LEAVE\n" );
    return hr;
} 

STDMETHODIMP CBoundBreakpoint::GetBreakpointResolution( IDebugBreakpointResolution2** ppBPResolution ) 
{ 
    //ATLTRACE( "CBoundBreakpoint::GetBreakpointResolution()\n" );
    HRESULT hr;  
    if( ppBPResolution )  
    {  
        if( m_bpState != BPS_DELETED )  
        {  
            hr = this->QueryInterface( IID_IDebugBreakpointResolution2, (void**)ppBPResolution );
        } else {  
            hr = E_BP_DELETED;  
        }  
    } else {  
        hr = E_INVALIDARG;  
    }  
    return hr;
} 

////////////////////////////////////////////////////////////////////////////// 
// IDebugBreakpointResolution2 
STDMETHODIMP CBoundBreakpoint::GetBreakpointType( BP_TYPE* pBPType ) 
{ 
    //ATLTRACE( "CBoundBreakpoint::GetBreakpointType()\n" );
    HRESULT hr;
    if( pBPType ) {
        *pBPType = BPT_CODE; 
        hr = S_OK;
    } else
        hr = E_INVALIDARG;
    return hr;   
} 

STDMETHODIMP CBoundBreakpoint::GetResolutionInfo( BPRESI_FIELDS dwFields, BP_RESOLUTION_INFO* pBPResolutionInfo ) 
{ 
    //ATLTRACE( "CBoundBreakpoint::GetResolutionInfo()\n" );
    if( !pBPResolutionInfo ) 
        return E_INVALIDARG;
    
    m_bpResolutionInfo.CopyTo( pBPResolutionInfo );
    return S_OK;
}

STDMETHODIMP CBoundBreakpoint::GetPosition( TEXT_POSITION *pPos )
{
    //ATLTRACE( "CBoundBreakpoint::GetPosition()\n" );
    if( !pPos )
        return E_INVALIDARG;

    pPos->dwLine    = m_BPPos.dwLine;
    pPos->dwColumn  = m_BPPos.dwColumn;
    return S_OK;
}

STDMETHODIMP CBoundBreakpoint::GetSymbol( DWORD **ppOut )
{
    CComQIPtr<IXBreakpoint> spXBP( m_spPendingBP );
    return spXBP->GetSymbol( ppOut );
}

STDMETHODIMP CBoundBreakpoint::IncrementHitCounter()
{
    m_dwHitCount += 1;
    return S_OK; 
}

